import webapp2
import structs
import json
import cgi
import random
from google.appengine.ext import db
from google.appengine.api import users
import datetime
from datetime import timedelta

#a method to unescape strings
def unescape(s):
    s = s.replace("&lt;", "<")
    s = s.replace("&gt;", ">")
    s = s.replace("&amp;", "&")
    return s

#a helper method to get all of the names from some object with a name field
def getNames(namedObject):
    names = []
    for obj in namedObject:
        if obj.name == "":
            names.append("Default")
        else:
            names.append(cgi.escape(obj.name))
    return names

#a helper method to make a user account after a person logs in
def userCheck(user, canTeach):
    if user and not structs.People.getPerson(user):
        userEntity = structs.People()
        userEntity.uid = user.user_id()
        userEntity.user = user
        userEntity.canTeach = canTeach
        userEntity.put()
    return user

#get the student responses of a list of answers
def getResponses(namedObject):
    names = []
    for obj in namedObject:
        names.append(cgi.escape(obj.studentResponse))                
    return names   
#get the dates of a list of answer objects
def getDates(namedObject):
    names = []
    for obj in namedObject:
        names.append(cgi.escape(obj.date))                
    return names

#a helper method to return all of a users courses
class GetCourse(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in
        if userCheck(user, True):    
            userEntity = structs.People.getPerson(user)
            #check if the user has permission to make a course
            if userEntity.canTeach:
                self.response.write(cgi.escape(json.dumps(getNames(db.get(userEntity.courses)))))     
            else:
                self.response.write("Sorry you cannot create a course.")
        else:
            self.response.write("Please log in.")
            
#a helper method to returning a course's categories
class GetCategory(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in
        if userCheck(user, True):    
            userEntity = structs.People.getPerson(user)
            #the course name
            courseName = unescape(self.request.get("name"))           
            #course = structs.Category.gql("WHERE name = :1 AND teacher = :2", courseName,
            #                        userEntity).get()            
            #an alternative
            course = structs.Course.gql("WHERE name = :1", courseName).get()
            #check if the user has permission to make a category in a course
            if course:
                self.response.out.write(cgi.escape(json.dumps(getNames(course.Categories))))     
            else:
                self.response.out.write("Sorry you cannot create a category.")
        else:
            self.response.out.write("Please log in.")

#to get student responses, this needs category, question
class GetAnswers(webapp2.RedirectHandler):    
    def post(self):
        user = users.get_current_user()
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            
            thisCategory = db.get(unescape(self.request.get("categoryKey")))
            thisQuestion = db.get(unescape(self.request.get("questionKey")))   
                     
            #can get responses only if the question is responseVisible, or if user is teacher
            if (thisQuestion.responsesVisible and thisQuestion.showData) or userEntity.uid == thisQuestion.teacher.uid:
                #fetch the data now
                answers = db.GqlQuery("SELECT * "
                                     "FROM Answer "
                                      "WHERE category = :1 AND question = :2",
                                      thisCategory, thisQuestion)
                info = []
                #invariant: all questions are in the right time range
                for answer in answers:
                    entry = {}
                    entry["timeElapsed"] = (answer.date - thisQuestion.date).seconds
                    entry["studentID"] = answer.student.uid
                    entry["studentResponse"] = answer.studentResponse
                    info.append(entry)
                buffer = timedelta(seconds=3)
                #if we should stop fetching, append a bogus "STOP" entry
                if datetime.datetime.utcnow() > (thisQuestion.endDate + buffer):
                    entry = {}
                    entry["timeElapsed"] = -999
                    entry["studentID"] = -999
                    entry["studentResponse"] = "F"
                    info.append(entry)
                self.response.out.write(cgi.escape(json.dumps(info)))                                
            else:
                self.response.out.write("Sorry, you cannot view these responses")
        else:
            self.response.out.write("Sorry, you are not logged in")            
            
#get the current question properties to determine what fields to display
class GetDisplays(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)           
            thisQuestion = db.get(self.request.get("questionKey"))                                         
            booleans = []
            booleans.append(thisQuestion.responsesVisible)
            booleans.append(thisQuestion.showData)
            if (userEntity.uid == thisQuestion.teacher.uid):
                booleans.append(True)
            else:
                booleans.append(False)               
            self.response.out.write(json.dumps(booleans))        
        else:
            self.response.out.write("Sorry, you are not logged in")

#set the question field properties, to eventually show or hide certain fields
class SetDisplays(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        if userCheck(user, True):     
            question = db.get(self.request.get("questionKey"))                    
            if self.request.get("editData") == "True":
                question.showData = (self.request.get("showData") == "True")
                self.response.out.write("Real-time data")
            if self.request.get("editResponses") == "True":
                question.responsesVisible = (self.request.get("responsesVisible") == "True") 
                self.response.out.write("Sharing screen with class")   
            question.put()                         
        else:
            self.response.out.write("Sorry, you are not logged in")
            
#a helper page for ajax requests to create a course
class CourseMaker(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):    
            userEntity = structs.People.getPerson(user)
            #check if the user has permission to make a course            
            if userEntity.canTeach:
                #make  sure another course doesn't have the same name
                #store the number of courses that have the name, only 0 or 1
                count = structs.Course.all().filter("name = ", unescape(self.request.get("cName"))).count()
                if count == 0:
                    courseName = unescape(self.request.get("cName"))
                    course = structs.Course(parent=userEntity.key())
                    course.teacher = userEntity
                    course.name = courseName
                    course.description = unescape(self.request.get("cDescription"))
                    #Make sure subtitle and name are right
                    if course.description == "" or course.name == "":
                        self.response.out.write("Please enter all fields.")
                    else:
                        #puts the course in and assigns it to the teacher
                        course.put()                          
                        userEntity.courses.append(course.key())
                        userEntity.put()                        
                        #creates the default category
                        category = structs.Category(parent=course.key())
                        category.name = "Default"
                        category.isDefault = True
                        category.course = course
                        category.put()
                        self.response.out.write("success")
                else:
                    self.response.out.write("Sorry, you need a unique course name.")
            #if the user lacks privileges to do so, complain
            else:
                self.response.out.write("Sorry, you cannot create courses.")
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper page for ajax requests to get all courses
class CourseLister(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):    
            userEntity = structs.People.getPerson(user)
            courses = []
            #pack all courses in array of associative array
            for course in db.get(userEntity.courses):
                aCourse = {}
                aCourse["name"] = course.name
                aCourse["description"] = course.description
                aCourse["key"] = str(course.key()) 
                courses.append(aCourse)
            #return a json object of courses
            self.response.out.write(cgi.escape(json.dumps(courses)))       
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper page for ajax requests to create a category
class CategoryMaker(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()        
        #check if the user is logged in
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            courseName = unescape(self.request.get("course"))
            if courseName == None or courseName == "":
                self.response.out.write("Please enter a valid course.")
                return
            query = db.GqlQuery("SELECT * FROM Course WHERE name = :1",
                                courseName) 
            course = query.get()            
            if not course:
                self.response.out.write("Please select a valid course.")
            #if the user teaches the course, allow them to create the course  
            #currently, course is a null item      
            if course and userEntity.uid == course.teacher.uid:
                #require a unique category name, ie no other categories in course
                #if this doesn't work, get all that have same name and
                #then for loop through and check if they share a name 
                count = structs.Category.gql("WHERE name = :1 AND course = :2"
                                             , unescape(self.request.get("name")), 
                                             course.key()).count()
                if count == 0:
                    category = structs.Category(parent=course.key())
                    category.name = unescape(self.request.get("name"))
                    category.subtitle = unescape(self.request.get("subtitle"))
                    category.isDefault = False
                    
                    category.course = course
                    if category.name == "" or category.subtitle == "":
                        self.response.out.write("Please enter a valid name or subtitle.")
                        return 
                    category.put()                    
                    self.response.out.write("success")
                else:
                    self.response.out.write("Sorry, you need a unique category name" 
                                            + " in this course.")
            else:
                self.response.out.write("Sorry, you do not teach this course.")
        else:
            self.response.out.write("Sorry, you are not logged in.")                  

#a helper function for ajax requests to get all categories
class CategoryLister(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            #old cold to retrieve the course value
            #courseName = self.request.get("course")
            #query = db.GqlQuery("SELECT * FROM Course WHERE name = :1",
            #                    courseName) 
            #course = query.get()
            course = db.get(unescape(self.request.get("courseKey")))
            courseName = course.name
            if course == None or courseName == "":
                self.response.out.write("Please enter a valid course.")
                return
            #if the user teaches the course, allow them to create the question
            if course.key() in userEntity.courses:
                categories = []
                #pack all categores in an array of associative arrays
                for category in course.Categories:
                    aCat = {}
                    aCat["name"] = category.name
                    aCat["subtitle"] = category.subtitle
                    aCat["key"] = str(category.key()) 
                    categories.append(aCat)
                #return categories in json
                self.response.out.write(cgi.escape(json.dumps(categories)))
            else:
                self.response.out.write("Sorry, you are not in this course.")
        else:
            self.response.out.write("Sorry, you are not logged in.")


#a helper page for ajax requests to create a question
#TODO: needs to include field assignments for time and isActive
class QuestionMaker(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            courseName = unescape(self.request.get("course"))
            query = db.GqlQuery("SELECT * FROM Course WHERE name = :1",
                                courseName) 
            course = query.get()           
            if course == None or courseName == "":
                self.response.out.write("Please enter a valid course.")
                return
            #the category this question will be in, 0 is temp holder for object
            category = 0
            for cat in course.Categories:
                if cat.name == unescape(self.request.get("category")):
                    category = cat
            if category == 0:
                self.response.out.write("Please enter a valid category.")
                return                               
            #if the user teaches the course, allow them to create the question
            if userEntity.uid == course.teacher.uid:
                question = structs.Question(parent=userEntity.key())
                question.teacher = course.teacher                
                question.name = unescape(self.request.get("qName"))              
                question.text = unescape(self.request.get("qText"))
                question.active = True
                question.responsesVisible = False
                question.showData = True
                options = []
                options.append(unescape(self.request.get("choiceA")))
                options.append(unescape(self.request.get("choiceB")))
                options.append(unescape(self.request.get("choiceC")))
                options.append(unescape(self.request.get("choiceD")))
                options.append(unescape(self.request.get("choiceE")))
                question.options = options
                question.answer = unescape(self.request.get("answer"))                
                if question.name == "" or question.text == "":
                    self.response.out.write("Please enter a name, text, and answers.")
                    return
                else:
                    for option in question.options:
                        if option == "":
                            self.response.out.write("Please enter all options.")
                            return
                #question.endDate = DateTime(datetime.now().seconds + 30)
                question.timeToAnswer = 60
                timeToAnswer = timedelta(seconds=question.timeToAnswer) 
                question.endDate = datetime.datetime.utcnow() + timeToAnswer                
                question.put()
                category.questions.append(question.key())
                category.put()
                self.response.out.write("success")
            else:
                self.response.out.write("Sorry, you do not teach this course.")
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper page for ajax requests to create a question
#TODO: needs to include field assignments for time and isActive
class QuestionLister(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            course = db.get(unescape(self.request.get("courseKey")))          
            if course == None:
                self.response.out.write("Please enter a valid course.")
                return
            #the category the questions are in, complain if not in course
            category = db.get(unescape(self.request.get("categoryKey")))
            categoryInCourse = False
            for cat in course.Categories:
                if cat.key() == category.key():
                    categoryInCourse = True
            if not categoryInCourse:
                self.response.out.write("Category not in this course.")
                return
            #if the user teaches the course, allow them to create the question
            if course.key() in userEntity.courses:
                questions = []
                #pack all categores in an array of associative arrays
                for question in db.get(category.questions):
                    aQuest = {}
                    aQuest["name"] = question.name
                    aQuest["text"] = question.text
                    aQuest["active"] = question.active
                    aQuest["visible"] = question.responsesVisible
                    aQuest["key"] = str(question.key())
                    questions.append(aQuest)
                #return questions in json
                self.response.out.write(cgi.escape(json.dumps(questions)))
            else:
                self.response.out.write("Sorry, you are not in this course")
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper page for ajax requests to create a question
#TODO: needs to include field assignments for time and isActive
class ClarificationMaker(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            courseName = unescape(self.request.get("course"))
            query = db.GqlQuery("SELECT * FROM Course WHERE name = :1",
                                courseName) 
            course = query.get()           
            if course == None or courseName == "":
                self.response.out.write("Please enter a valid course.")
                return              
            #if the user teaches the course, allow them to create the question
            if userEntity.uid != course.teacher.uid:
                clarification = structs.Clarification(parent=course.teacher.key())
                clarification.course = course
                clarification.name = unescape(self.request.get("cName"))
                clarification.text = unescape(self.request.get("cText"))   
                clarification.student = userEntity             
                if clarification.name == "" or clarification.text == "":
                    self.response.out.write("Please enter a name and text.")
                    return
                #question.endDate = DateTime(datetime.now().seconds + 30)
                clarification.put()
                self.response.out.write("success")
            else:
                self.response.out.write("Sorry, you teach this course.")
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper page for ajax requests to create a question
#TODO: needs to include field assignments for time and isActive
class ClarificationLister(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            course = db.get(unescape(self.request.get("courseKey")))          
            if course == None:
                self.response.out.write("Please enter a valid course.")
                return
            #if the user teaches the course, allow them to create the clarification
            if course.teacher.uid == userEntity.uid:
                clarifications = []
                #pack all categores in an array of associative arrays
                for clarification in structs.Clarification.gql("WHERE course = :1 ORDER BY date DESC", db.get(unescape(self.request.get("courseKey")))):
                    aClar = {}
                    aClar["name"] = clarification.name
                    aClar["text"] = clarification.text
                    aClar["student"] = clarification.student.user.nickname()
                    aClar["key"] = str(clarification.key())
                    clarifications.append(aClar)
                #return questions in json
                self.response.out.write(cgi.escape(json.dumps(clarifications)))
            else:
                self.response.out.write("Sorry, you are not in this course")
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper page for ajax requests to create a question
class QuestionAnswer(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            #old cold to retrieve the course value
            #courseName = self.request.get("course")
            #query = db.GqlQuery("SELECT * FROM Course WHERE name = :1",
            #                    courseName) 
            #course = query.get()
            course = db.get(unescape(self.request.get("courseKey")))
            courseName = course.name
            if course == None or courseName == "":
                self.response.out.write("Please enter a valid course.")
                return
            #the category this question will be in, 0 is temp holder for object
            category = 0
            target = db.get(unescape(self.request.get("categoryKey")))
            for cat in course.Categories:
                if cat.name == target.name:
                    category = cat
            #if the user teaches the course, allow them to create the question
            if userEntity.uid == course.teacher.uid:
                self.response.out.write("A teacher cannot answer their own questions.")
            elif course.key() in userEntity.courses:
                #answer = structs.Answer.gql("WHERE student = :1", userEntity).get()
                #if not answer: #create a new answer query
                    #answer = structs.Answer(parent=userEntity.key())        
                answer = structs.Answer(parent=userEntity.key())
                answer.category = category
                thisQuestion = db.get(unescape(self.request.get("questionKey")))
                answer.question = thisQuestion
                answer.student = userEntity.key()
                                                          
                #answer.category = self.request.get("category")
                #answer.question = self.request.get("question")                
                answer.studentResponse = self.request.get(unescape("studentResponse"))
                
                
                #self.response.out.write((datetime.datetime.now() - thisQuestion.endDate).seconds)                
                #self.response.out.write((datetime.datetime.now() - thisQuestion.date).seconds)                
                if (datetime.datetime.utcnow() > thisQuestion.endDate):       
                    self.response.out.write("Sorry, time has expired")                    
                else:
                    answer.put()                
                    self.response.out.write("Success")
            else:
                self.response.out.write("Sorry, you do not teach this course")
        else:
            self.response.out.write("Sorry, you are not logged in.")

#a helper to connect a user to a course
class FindQuestion(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            cKey = structs.CKey.gql("WHERE uKey = :1 ORDER BY date DESC", unescape(self.request.get("courseKey"))).get()
            if not cKey:
                self.response.out.write("No such course exists.")
                return
            course = cKey.course
            for student in course.students():
                if student.uid == userEntity.uid:
                    self.response.out.write("You are already in this course.")
                    return
            if course.teacher.uid != userEntity.uid:
                userEntity.courses.append(course.key())
                userEntity.put()
                self.response.out.write("success")
            else:
                self.response.out.write("You teach this course. You cannot add it again.")
                
#a helper method to create question finders, QKeys
class CKeyMaker(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in
        if userCheck(user, True):
            courseName = unescape(self.request.get("course"))
            query = db.GqlQuery("SELECT * FROM Course WHERE name = :1",
                                courseName) 
            course = query.get()
            if course == None or courseName == "":
                self.response.out.write("Please enter a valid course.")
                return
            ckey = structs.CKey()
            ckey.date
            ckey.course = course
            #create a random alphanumeric key  of length 5
            uKey = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
            lastKey = structs.CKey.gql("WHERE uKey = :1 ORDER BY date DESC", uKey).get()
            if lastKey:
                difftime = ckey.date - structs.CKey.gql("WHERE uKey = :1 ORDER BY date DESC", uKey).get().date 
                #ensure key is unique for last 5 minutes
                while difftime.total_seconds() < 300:
                    uKey = ''.join(random.choice('0123456789ABCDEF') for i in range(16))
                    difftime = ckey.date - structs.CKey.gql("WHERE uKey = :1 ORDER BY date DESC", uKey).get().date
            ckey.uKey = uKey
            ckey.put()
            self.response.out.write(ckey.uKey)        

#a helper to return all the right answers in a category for a course
class CourseDistribution(webapp2.RedirectHandler):
    def post(self):
        user = users.get_current_user()
        #check if the user is logged in        
        if userCheck(user, True):
            userEntity = structs.People.getPerson(user)
            courseKey = unescape(self.request.get("courseKey"))
            course = db.get(courseKey)           
            if course == None:
                self.response.out.write("Please enter a valid course.")
                return                              
            #if the user teaches the course, allow them to create the question
            if userEntity.uid == course.teacher.uid:
                #the number of right answers in each category
                rightAnswers = []
                for cat in course.Categories:
                    #number answered right and number answered total
                    numRight = 0
                    numTotal = 0
                    for answer in cat.Answers:
                        numTotal += 1
                        if answer.studentResponse == answer.question.answer:
                            numRight += 1
                    rightAnswers.append([cat.name, numRight, numTotal])
                self.response.out.write(cgi.escape(json.dumps(rightAnswers)))
            else:
                self.response.out.write("Sorry, you do not teach this course.")
        else:
            self.response.out.write("Sorry, you are not logged in.")
        
